﻿using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.IO;
using System.Reflection;

namespace GFluentCode.Tools
{
    /// <summary>
    /// 动态代码帮助类
    /// </summary>
    public class CodeCompilerHelper
    {        
        private static string _code;

        private static CompilerResults _cr;
        private static MethodInfo _mi;
        private static object _funcInstance;
        private static string _curMethodName;

        /// <summary>
        /// 运行动态代码
        /// </summary>
        /// <param name="namespaceName">命名空间</param>
        /// <param name="className">类名</param>
        /// <param name="funcName">方法名</param>
        /// <param name="dlls">引用库</param>
        /// <param name="args">参数</param>
        /// <returns></returns>
        public object RunFunc(string namespaceName, string className, string funcName,string[] dlls, object[] args)
        {
            #region 编译环节
            if (_cr is null)
            {
                // 1.CSharpCodePrivoder
                CSharpCodeProvider objCSharpCodePrivoder = new CSharpCodeProvider();

                // 2.ICodeComplier
#pragma warning disable CS0618 // 类型或成员已过时
                ICodeCompiler objICodeCompiler = objCSharpCodePrivoder.CreateCompiler();
#pragma warning restore CS0618 // 类型或成员已过时

                // 3.CompilerParameters
                CompilerParameters objCompilerParameters = new CompilerParameters();

                if (dlls == null || dlls.Length <= 0)
                {
                    objCompilerParameters.ReferencedAssemblies.Add("System.dll");
                    objCompilerParameters.ReferencedAssemblies.Add("System.Data.dll");
                    objCompilerParameters.ReferencedAssemblies.Add("System.Xml.dll");
                    objCompilerParameters.ReferencedAssemblies.Add("System.Core.dll");
                    objCompilerParameters.ReferencedAssemblies.Add("mscorlib.dll");
                }
                else
                {
                    foreach(var dll in dlls)
                    {
                        objCompilerParameters.ReferencedAssemblies.Add(dll);
                    }
                }
                objCompilerParameters.GenerateExecutable = false;
                objCompilerParameters.GenerateInMemory = true;

                // 4.CompilerResults
                _cr = objICodeCompiler.CompileAssemblyFromSource(objCompilerParameters, _code);

                if (_cr.Errors.HasErrors)
                {
                    var errStr = "编译错误：";
                    foreach (CompilerError err in _cr.Errors)
                    {
                        //Console.WriteLine(err.ErrorText);
                        errStr += err.ErrorText + "\r\n";
                    }
                    return errStr;
                }
                else
                {
                    // 通过反射，调用HelloWorld的实例
                    Assembly objAssembly = _cr.CompiledAssembly;
                    _funcInstance = objAssembly.CreateInstance($"{namespaceName}.{className}");
                    _mi = _funcInstance.GetType().GetMethod(funcName);
                    _curMethodName = funcName;
                }
            }

            #endregion

            if (_mi is null)
                return "无内存代码";
            
            if(!_curMethodName.Equals(funcName))
            {
                _mi = _funcInstance.GetType().GetMethod(funcName);
                _curMethodName = funcName;
            }

            return _mi.Invoke(_funcInstance, args);
        }

        /// <summary>
        /// 清除
        /// </summary>
        public static void Clear()
        {
            _funcInstance = null;
            _mi = null;
            _cr = null;
        }

        /// <summary>
        /// 加载代码
        /// </summary>
        /// <param name="code"></param>
        public static void Make(string code)
        {
            _code = code;
        }
    }
}